package edu.upf.grupE.Video;

import java.awt.*;
import java.net.*;
import java.util.Vector;

import javax.media.*;
import javax.media.rtp.*;
import javax.media.rtp.event.*;
import javax.media.protocol.DataSource;
import javax.media.control.BufferControl;


public class AVReceive2 implements ReceiveStreamListener, SessionListener, ControllerListener {
    String sessions[] = null;
    RTPManager mgrs[] = null;
    Vector playerWindows = null;

    boolean dataReceived = false;
    Object dataSync = new Object();


    public AVReceive2(String sessions[]) {
	this.sessions = sessions;
    boolean a= this.initialize();
    }

    /*
     * Method that initializes the receiver.
     */
    protected boolean initialize() {
        try {
            InetAddress ipAddr;
            SessionAddress localAddr = new SessionAddress();
            SessionAddress destAddr;

            mgrs = new RTPManager[sessions.length];
            playerWindows = new Vector();

            SessionLabel session;

	    // Create the necessaries RTP sessions.
	    for (int i = 0; i < sessions.length; i++) {

            try {
                session = new SessionLabel(sessions[i]);
            } catch (IllegalArgumentException e) {
                System.err.println("Error al escribir la ip address: " + sessions[i]);
                return false;
            }

            System.err.println("  - Abrir RTP session for: addr: " + session.addr + " port: " + session.port + " ttl: " + session.ttl);

            mgrs[i] = (RTPManager) RTPManager.newInstance();
            mgrs[i].addSessionListener(this);
            mgrs[i].addReceiveStreamListener(this);

            ipAddr = InetAddress.getByName(session.addr);

            // If the local and destination address
            // have or not have the same value.
            if( ipAddr.isMulticastAddress()) {
                localAddr= new SessionAddress( ipAddr,
                               session.port,
                               session.ttl);
                destAddr = new SessionAddress( ipAddr,
                               session.port,
                               session.ttl);
            } else {
                localAddr= new SessionAddress( InetAddress.getLocalHost(),
                                   session.port);
                        destAddr = new SessionAddress( ipAddr, session.port);
            }

            //initialize another mgrs with another address.
            mgrs[i].initialize(localAddr);
            BufferControl bc = (BufferControl)mgrs[i].getControl("javax.media.control.BufferControl");
            if (bc != null)
                bc.setBufferLength(350);

                mgrs[i].addTarget(destAddr);
        }

        } catch (Exception e){
            System.err.println("Cannot create the RTP Session: " + e.getMessage());
            return false;
        }

        // Wait for data to arrive before change the application (30 secs).
        long then = System.currentTimeMillis();
        long waitingPeriod = 30000;  

        try{
            synchronized (dataSync) {
                while (!dataReceived &&
                    System.currentTimeMillis() - then < waitingPeriod) {
                    if (!dataReceived)
                    System.err.println("  - Waiting for RTP data to arrive...");
                    dataSync.wait(1000);
                }
            }
        } catch (Exception e) {}

        // If there was no data during this period
        // we call the close function to finalize the program.
        if (!dataReceived) {
            System.err.println("No RTP data was received.");
            close();
            return false;
        }

        return true;
    }

    /**
     * Close the players and the session managers.
     */
    protected void close() {
        // Close the players.
        for (int i = 0; i < playerWindows.size(); i++) {
            try {
            ((PlayerWindow)playerWindows.elementAt(i)).close();
            } catch (Exception e) {}
        }

        playerWindows.removeAllElements();

        // Close the RTP session.
        for (int i = 0; i < mgrs.length; i++) {
            if (mgrs[i] != null) {
                    mgrs[i].removeTargets( "Closing session from AVReceive2");
                    mgrs[i].dispose();
            mgrs[i] = null;
            }
        }
    }

    // Create a playerWindow with the data of the video channel.
    PlayerWindow find(Player p) {
        for (int i = 0; i < playerWindows.size(); i++) {
            PlayerWindow pw = (PlayerWindow)playerWindows.elementAt(i);
            if (pw.player == p)
            return pw;
        }
        return null;
    }

    // Create a playerWindow with the data of the audio channel
    PlayerWindow find(ReceiveStream strm) {
        for (int i = 0; i < playerWindows.size(); i++) {
            PlayerWindow pw = (PlayerWindow)playerWindows.elementAt(i);
            if (pw.stream == strm)
            return pw;
        }
        return null;
    }

    public synchronized void update(SessionEvent evt) {
        if (evt instanceof NewParticipantEvent) {
            Participant p = ((NewParticipantEvent)evt).getParticipant();
            System.err.println("  - A new participant had just joined: " + p.getCNAME());
        }
    }

    public synchronized void update(ReceiveStreamEvent evt) {

        Participant participant = evt.getParticipant();	
        ReceiveStream stream = evt.getReceiveStream();

        if (evt instanceof RemotePayloadChangeEvent) {
            System.err.println("  - Received an RTP PayloadChangeEvent.");
            System.err.println("Sorry, cannot handle payload change.");
            System.exit(0);
        }
        else if (evt instanceof NewReceiveStreamEvent) {

            try {
            stream = ((NewReceiveStreamEvent)evt).getReceiveStream();
            DataSource ds = stream.getDataSource();

            // Find out the formats.
            RTPControl ctl = (RTPControl)ds.getControl("javax.media.rtp.RTPControl");
            if (ctl != null){
                System.err.println("  - Recevied new RTP stream: " + ctl.getFormat());
            } else
                System.err.println("  - Recevied new RTP stream");

            if (participant == null)
                System.err.println("      The sender of this stream had yet to be identified.");
            else {
                System.err.println("      The stream comes from: " + participant.getCNAME());
            }

            // Create a player to pass data source.
            Player p = javax.media.Manager.createPlayer(ds);
            if (p == null)
                return;

            p.addControllerListener(this);
            p.realize();
            PlayerWindow pw = new PlayerWindow(p, stream);
            playerWindows.addElement(pw);

            // Notify intialize() that a new stream has arrived.
            synchronized (dataSync) {
                dataReceived = true;
                dataSync.notifyAll();
            }

            } catch (Exception e) {
            System.err.println("NewReceiveStreamEvent exception " + e.getMessage());
            return;
            }

        } else if (evt instanceof StreamMappedEvent) {

            if (stream != null && stream.getDataSource() != null) {
            DataSource ds = stream.getDataSource();
            // Find out the formats.
            RTPControl ctl = (RTPControl)ds.getControl("javax.media.rtp.RTPControl");
            System.err.println("  - The previously unidentified stream ");
            if (ctl != null)
                System.err.println("      " + ctl.getFormat());
            System.err.println("      had now been identified as sent by: " + participant.getCNAME());
             }
        } else if (evt instanceof ByeEvent) {

            System.err.println("  - Got \"bye\" from: " + participant.getCNAME());
             PlayerWindow pw = find(stream);
             if (pw != null) {
            pw.close();
            playerWindows.removeElement(pw);
             }
        }
    }

    /*
     * Method that controls the several events generated for the players.
     */
    public synchronized void controllerUpdate(ControllerEvent ce) {
        Player p = (Player)ce.getSourceController();

        if (p == null)
            return;

        if (ce instanceof RealizeCompleteEvent) {
            PlayerWindow pw = find(p);
            if (pw == null) {
                System.err.println("Internal error!");
                System.exit(-1);
            }
            pw.initialize();
            pw.setVisible(true);
            p.start();
        }

        if (ce instanceof ControllerErrorEvent) {
            p.removeControllerListener(this);
            PlayerWindow pw = find(p);
            if (pw != null) {
                pw.close();
                playerWindows.removeElement(pw);
            }
            System.err.println("AVReceive2 internal error: " + ce);
        }

    }


    /**
     * Class that parses the sessions (ip addresses and ports).
     */
    class SessionLabel {
        public String addr = null;
        public int port;
        public int ttl = 1;

        SessionLabel(String session) throws IllegalArgumentException {

            int off;
            String portStr = null, ttlStr = null;

            if (session != null && session.length() > 0) {
                while (session.length() > 1 && session.charAt(0) == '/')
                    session = session.substring(1);

                    off = session.indexOf('/');
                    if (off == -1) {
                        if (!session.equals(""))
                        addr = session;
                    } else {
                        addr = session.substring(0, off);
                        session = session.substring(off + 1);
                        off = session.indexOf('/');
                        if (off == -1) {
                            if (!session.equals(""))
                                portStr = session;
                        } else {
                            portStr = session.substring(0, off);
                            session = session.substring(off + 1);
                            off = session.indexOf('/');
                            if (off == -1) {
                                if (!session.equals(""))
                                ttlStr = session;
                            } else {
                                ttlStr = session.substring(0, off);
                            }
                        }
                    }
            }

            if (addr == null)
            throw new IllegalArgumentException();

            if (portStr != null) {
                try {
                    Integer integer = Integer.valueOf(portStr);
                    if (integer != null)
                    port = integer.intValue();
                } catch (Throwable t) {
                    throw new IllegalArgumentException();
                }
            } else
                throw new IllegalArgumentException();

                if (ttlStr != null) {
                    try {
                        Integer integer = Integer.valueOf(ttlStr);
                        if (integer != null)
                        ttl = integer.intValue();
                    } catch (Throwable t) {
                        throw new IllegalArgumentException();
                    }
                }
        }
    }


    /**
     * Graphical interface of the audio player panel.
     */
    class PlayerWindow extends Frame {

        Player player;
        ReceiveStream stream;

        PlayerWindow(Player p, ReceiveStream strm) {
            player = p;
            stream = strm;
        }

        public void initialize() {
            add(new PlayerPanel(player));
        }

        public void close() {
            player.close();
            setVisible(false);
            dispose();
        }

        public void addNotify() {
            super.addNotify();
            pack();
        }
    }


    /**
     * Graphical interface of the video player panel.
     */
    class PlayerPanel extends Panel {

        Component vc, cc;

        PlayerPanel(Player p) {
            setLayout(new BorderLayout());
            if ((vc = p.getVisualComponent()) != null)
            add("Center", vc);
            if ((cc = p.getControlPanelComponent()) != null)
            add("South", cc);
        }

        // Introduce the adequate size to the format video.
        public Dimension getPreferredSize() {
            int w = 0, h = 0;
            if (vc != null) {
            Dimension size = vc.getPreferredSize();
            w = size.width;
            h = size.height;
            }
            if (cc != null) {
            Dimension size = cc.getPreferredSize();
            if (w == 0)
                w = size.width;
            h += size.height;
            }
            if (w < 160)
            w = 160;
            return new Dimension(w, h);
        }
    }

}// end of AVReceive2



